#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
log module
"""

import json
import pathlib
import yaml
import uuid
import logging
import logging.config
from src.common.context import ctx_http_req, ctx_http_resp
import tornado


logger = logging.getLogger()


def log_http_context(func):
    """ 日志上下文(http) """

    def _log(*args, **kwargs):
        """ 日志上下文 """
        if len(args) == 0:
            return func(*args, **kwargs)
        # 获取请求对象
        req = getattr(args[0], 'request', None)
        if req is None or not isinstance(req, tornado.httputil.HTTPServerRequest):
            return func(*args, **kwargs)
        try:
            request_body = tornado.escape.json_decode(req.body)
            log_id = request_body.get('log_id', "")
            if log_id == "":
                log_id = create_request_id()
                request_body['log_id'] = log_id
            fields = {
                'log_id': request_body.get('log_id', ""),
                'session_id': request_body.get('session_id', ""),
                'user_id': request_body.get('user_id', ""),
                'method': request_body.get('method', ""),
                'uri': req.uri,
            }
            fields['request_body'] = request_body


        except Exception as e:
            logger.error(f'get http fields error: {e}')
        # 获取并设置请求上下文
        if log_id != "":
            ctx_http_req.set(fields)
        # result = func(*args, **kwargs)
        # 获取并设置响应上下文(这里不设置了，需要的情况由外部设置)
        # ctx_http_resp.set(result)

        return func(*args, **kwargs)

    return _log

def init_log(log_config_path):
    """ init log """
    # 加载配置
    log_config = yaml.load(open(log_config_path, 'r'), yaml.CLoader)
    # 创建日志目录
    log_path_set = set()
    for v in log_config.get('handlers', {}).values():
        filename = v.get('filename', '')
        if filename != '':
            path = pathlib.Path(filename)
            log_path_set.add(path.parent)
    for path in log_path_set:
        path.mkdir(parents=True, exist_ok=True)
    # 初始化日志
    logging.config.dictConfig(log_config)


class TraceLogFilter(logging.Filter):
    """
    A filter which injects context-specific information into logs and ensures
    that only information for a specific webapp is included in its log
    """

    def __init__(self, app_name: str, module_name: str):
        # self.app = app
        super().__init__('trace_log_filter')
        self.app_name = app_name
        self.module_name = module_name
        pass

    def filter(self, record):
        req = get_or_set_req()
        record.appname = self.app_name  # 应用名称
        record.module = self.module_name  # 模块名称
        record.data = getattr(record, 'data', '')  # 扩展数据

        record.log_id = req.get('log_id', '')  # 日志logid
        record.session_id = req.get('session_id', '')  # 会话id
        record.user_id = req.get('user_id', '')  # 用户id
        if not record.log_id:
            message = record.getMessage()
            start = message.find('{')
            end = message.rfind('}')
            if start != -1 and end != -1:
                try:
                    json_str = message[start:end + 1]
                    format_json = json.loads(json_str)
                    record.log_id = format_json.get('log_id', '')
                    if not record.session_id:
                        record.session_id = format_json.get('session_id', '')
                    if not record.user_id:
                        record.user_id = format_json.get('user_id', '')
                except:
                    pass
        return True


class AccessLogFilter(logging.Filter):
    """
    A filter which injects context-specific information into logs and ensures
    that only information for a specific webapp is included in its log
    """

    def __init__(self, app_name: str, module_name: str):
        # self.app = app
        super().__init__('access_log_filter')
        self.app_name = app_name
        self.module_name = module_name
        self.filter_prefix = ['200 GET /metrics', '404 GET /rpc_metrics', '200 GET /health']
        pass

    def filter(self, record):
        # 过滤掉不需要记录的access log
        for prefix in self.filter_prefix:
            if record.getMessage().startswith(prefix):
                return False
        # access log
        req = get_or_set_req()
        resp_body = get_resp()

        req_str = ""
        resp_str = ""
        try:
            max_len = 4096
            req_body = req.get('request_body', None)
            req_str = json.dumps(req_body, ensure_ascii=False)
            resp_str = json.dumps(resp_body, ensure_ascii=False)
            if len(req_str) > max_len:
                req_str = req_str[:max_len] + '...'
            if len(resp_str) > max_len:
                resp_str = resp_str[:max_len] + '...'

        except Exception as e:
            logger.error(f'handle access log fields error: {e}, request:{req_body}, response:{resp_body}')

        # 常规日志字段
        record.appname = self.app_name  # 应用名称
        record.module = self.module_name  # 模块名称
        record.data = getattr(record, 'data', '')  # 扩展数据

        record.log_id = req.get('log_id', '')  # 日志logid
        record.session_id = req.get('session_id', '')  # 会话id
        record.user_id = req.get('user_id', '')  # 用户id
        record.method = req.get('method', '')  # 请求方法
        record.api = req.get('uri', '')  # uri
        record.request = req_str  # 请求数据
        record.response = resp_str  # 响应数据
        record.status = 0  # 状态码
        record.latency = ""  # 延迟
        if len(record.args) >= 3:
            record.status = record.args[0]
            # record.latency = "%.2fms" % record.args[2]
            record.latency = "%.2f" % record.args[2]

        # 业务日志字段
        record.intention = ""  # 意图
        record.query = ""  # 问题
        record.answer = ""  # 答案
        record.delay = "" 

        return True


def create_request_id() -> str:
    """ 生成请求ID """
    return str(uuid.uuid4())


def get_or_set_req():
    """ 获取请求数据 """
    req = ctx_http_req.get(None)
    if req is None:
        req = {
            'log_id': "",
        }
        ctx_http_req.set(req)

    return req


def get_resp():
    """ 获取响应数据 """
    resp = ctx_http_resp.get(None)
    return resp
